/**
 * @file    pc_comm.c
 * @brief   proto buf协议通信服务
 * @details This is the detail description.
 * @author  wang-haoyu
 * @date    2025年2月21日 (created)
 * @version 0.0.2
 * @par Copyright (C):
 *          HangZhou HuaYi Science and Technology Ltd. All Rights Reserved.
 * @par Encoding:
 *          GB2312
 * @par Description        :
 * 1. Hardware Descriptions:
 *      None.
 * 2. Program Architecture:
 *      None.
 * 3. File Usage:
 *      None.
 * 4. Limitations:
 *      None.
 * 5. Else:
 *      None.
 * @par Modification:
 * -# Date          : 2025年2月21日;
 * Revision         : 0.0.1;
 * Author           : wang-haoyu;
 * Contents         : first update
 * 
 * -# Date          : 2025年6月30日;
 * Revision         : 0.0.2;
 * Author           : wang-haoyu;
 * Contents         : refactor pc comm architecture
 * 
 * -# Date          : 2025年7月30日;
 * Revision         : 0.0.3;
 * Author           : wang-haoyu;
 * Contents         : pc comm File layering
 */
 
/* =============================================================================
 *                                header files
 * ========================================================================== */
#include "pc_comm.h"
#include "pc_comm_fifo.h"
#include "pc_comm_frame.h"
#include "project_config.h"

#include "pc_comm_server_task.h"
#include "pc_comm_single_task.h"
#define LOG_TAG "pc_comm"
#include "ulog.h"

/* =============================================================================
 *                              macros & constants
 * ========================================================================== */

/* =============================================================================
 *                          data structure definitions
 * ========================================================================== */
static pc_comm_frame_t pc_frame;
static rt_device_t pc_pccomm_serial;
static rt_sem_t s_pc_comm_sem_rx_obj;
static rt_timer_t pc_comm_timer;

static pccomm_fifo_port_t pccomm_fifo_port = 
{
	.fifo_name = PC_COMM_FIFO_NAME,
	.fifo_size = PC_COMM_FIFO_SIZE,
};
/* =============================================================================
 *                         static variable definitions
 * ========================================================================== */
static bool pc_comm_timeout_flag = false;
static bool pc_switch_sta = true;
static rt_uint8_t pc_comm_connect_sta = 0;
/* =============================================================================
 *                         static function definitions
 * ========================================================================== */
static void pc_comm_sendto_thread_entry(void *parameter);
static void pc_comm_recv_thread_entry(void *parameter);
static void pc_comm_unpack_thread_entry(void *parameter);

/*
 * 串口接收回调函数
 *
 * Parameter:
 *   Input:
 *   Output:
 * Returns:
 */
static rt_err_t uart_recv_byte_callback(rt_device_t dev, rt_size_t size)
{
	if(0 < size)
	{
		rt_sem_release(s_pc_comm_sem_rx_obj);
	}
	return RT_EOK;
}

/*
 * 串口接收超时回调函数
 *
 * Parameter:
 *   Input:
 *   Output:
 * Returns:
 */
static void pc_comm_timeout_state(void *parameter)
{
	rt_sem_release(s_pc_comm_sem_rx_obj);
	pc_comm_timeout_flag = true;
	rt_timer_start(pc_comm_timer);
}

/*
 * proto buf通信发送线程
 *
 * Parameter:
 *   Input:
 *   Output:
 * Returns:
 */
static void pc_comm_sendto_thread_entry(void *parameter)
{
	while(1)
	{	
		if(true == pc_switch_sta)
		{
            pc_comm_single_update_task_processing(pc_pccomm_serial);
            pc_comm_server_ack_task_processing(pc_pccomm_serial);
		}
		rt_thread_mdelay(PC_COMM_SEND_THREAD_MDELAY);
	}
}

/*
 * proto buf通信接收线程
 *
 * Parameter:
 *   Input:
 *   Output:
 * Returns:
 */
static void pc_comm_recv_thread_entry(void *parameter)
{
	rt_size_t recv_cnt = 0;
	rt_uint8_t buf[512] = {0};
	while(1)
	{
		rt_sem_take(s_pc_comm_sem_rx_obj,RT_WAITING_FOREVER);
		if(true == pc_switch_sta)
		{
			recv_cnt = rt_device_read(pc_pccomm_serial, 0, buf, sizeof(buf));
			if(0 < recv_cnt)
			{
				pc_comm_fifo_write(&pccomm_fifo_port, buf, recv_cnt);
	
				if(pc_comm_connect_sta != PC_CONNECT)
					LOG_E("PC connect");
				rt_timer_start(pc_comm_timer);
				pc_comm_connect_sta = PC_CONNECT;
				pc_comm_timeout_flag = false;
			}
			else if(true == pc_comm_timeout_flag)
			{
				if(pc_comm_connect_sta != PC_WAIT_CONNECT)
					LOG_E("PC not connect");
				
				pc_comm_fifo_clean(&pccomm_fifo_port);
				pc_comm_connect_sta = PC_WAIT_CONNECT;
			}
		}
		else
		{
			pc_comm_connect_sta = PC_POWEROFF;
		}
	}
}

/*
 * proto buf通信解包线程
 *
 * Parameter:
 *   Input:
 *   Output:
 * Returns:
 */
static void pc_comm_unpack_thread_entry(void *parameter)
{
	while(1)
	{	
		if(E_NO_ERR == pc_comm_unpark_frame_state(pccomm_fifo_port,&pc_frame))
		{
            pc_comm_single_set_task_processing(&pc_frame);
            pc_comm_server_request_task_processing(&pc_frame);
		}
		rt_thread_mdelay(PC_COMM_UNPACK_THREAD_MDELAY);
	}
}

/*
 * 工控机通信连接状态反馈
 *
 * Parameter:
 *   Input:
 *   Output:
 * Returns:
 *		-连接状态
 */
rt_uint8_t pc_comm_connect_state(void)
{
	return pc_comm_connect_sta;
}

/*
 * 工控机开关机状态设置
 *
 * Parameter:
 *   Input:
 *		-sta_set：设置状态
 *   Output:
 * Returns:
 */
void pc_switch_set_state(bool sta_set)
{
	rt_base_t level;
	level = rt_hw_interrupt_disable();
	pc_switch_sta = sta_set;
	rt_hw_interrupt_enable(level);
}

/*
 * protobuf通信初始化函数
 *
 * Parameter:
 *   Input:
 *   Output:
 * Returns:
 */
int pc_comm_init(void)
{
	struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
	PCCOMM_FIFO_ENOERR_CODE status = 0;
	
	pc_pccomm_serial = rt_device_find(PC_PCCOMM_UART);
	if(!pc_pccomm_serial)
	{
		rt_kprintf("open uart2 failed\n");
		return -1;
	}
	
	rt_device_control(pc_pccomm_serial, RT_DEVICE_CTRL_CONFIG, &config);
	rt_device_open(pc_pccomm_serial, RT_DEVICE_FLAG_DMA_RX  | RT_DEVICE_FLAG_DMA_TX);
	rt_device_set_rx_indicate(pc_pccomm_serial,uart_recv_byte_callback);

	status = pc_comm_fifo_creat(&pccomm_fifo_port);
    if(status != 0)
    {
        LOG_E("fifo creat faild");
        return -1;
    }
	
	pc_comm_server_task_init();

	s_pc_comm_sem_rx_obj = rt_sem_create(PC_COMM_RX_SEM_NAME,0,RT_IPC_FLAG_FIFO);
	if(s_pc_comm_sem_rx_obj == RT_NULL)
		LOG_E("sem creat faild");

	pc_comm_timer = rt_timer_create(PC_COMM_TIMER_NAME, pc_comm_timeout_state,RT_NULL, 2000,PC_COMM_TIMER_FLAG);
	if (pc_comm_timer != RT_NULL) 
		rt_timer_start(pc_comm_timer);

	rt_thread_t sendto_thread = rt_thread_create("pc_send", pc_comm_sendto_thread_entry, RT_NULL, PC_COMM_SEND_THREAD_PARAM);
	if(sendto_thread != RT_NULL)
		rt_thread_startup(sendto_thread);
	else
		goto __exit;
	
	rt_thread_t recv_thread = rt_thread_create("pc_recv", pc_comm_recv_thread_entry, RT_NULL, PC_COMM_RECV_THREAD_PARAM);
	if(recv_thread != RT_NULL)
		rt_thread_startup(recv_thread);
	else
		goto __exit;
	
	rt_thread_t unpack_thread = rt_thread_create("pc_unpack", pc_comm_unpack_thread_entry, RT_NULL, PC_COMM_UNPACK_THREAD_PARAM);
	if(unpack_thread != RT_NULL)
		rt_thread_startup(unpack_thread);
	else
		goto __exit;
	
	return 0;

__exit:
	if(sendto_thread)
		rt_thread_delete(sendto_thread);
	if(recv_thread)
		rt_thread_delete(recv_thread);
	if(unpack_thread)
		rt_thread_delete(unpack_thread);
}

/*******************************************************************************
 *                                  EOF
 ******************************************************************************/
